Nhibernate batch query timeout问题的解决
最近项目中遇到了一个奇怪的问题。在调用Nhibernate去更新数据库的时候不定期的会出现timeout exception:NHibernate.HibernateException: An exception occurred when executing batch queries ---> System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
由于之前没有出现过此类问题而且代码和环境都没有变过,所以首先想到的是数据库的问题导致的。于是叫上DBA一起把数据库日志翻了一遍,然而并没发现什么异常,最终归结为数据库性能问题,他们也没什么办法…
此刻一句名言飘荡在我的脑海之中Fuck the world if you are rich, otherwise fuck yourself.
好吧,从程序上入手看看有没有什么办法规避或者解决这个问题。首先重新查看了我们自己出错的日志,发现发生executing batch queries异常的时间都在30~35 sec,而这个异常是指执行数据库语句时抛的异常。而我们在Nhibernate的配置文件hibernate.cfg.xml中所配置的command_timeout是300sec,<property name="command_timeout">300</property>
。为什么这个配置没有生效呢?
在进行了海量搜索以及验证之后,终于fuck myself success。总结如下:
两个对应关系
- Connect String中加入的ConnectTimeOut <<========>> ADO.NET中SqlConnection.ConnectionTimeout:等待连接打开所需的时间(以秒为单位)。 默认值为 15 秒。detail
- Nhibernate中配置的IDbCommand的CommandTimeout <<========>> ADO.NET中SqlCommand.CommandTimeout:等待命令执行所需的时间(以秒为单位)。 默认值为 30 秒。detail
问题所在
通过搜索发现在StackOver上也有人遇到过同样的问题。总结了一下大概意思是说:在设置了
为了验证这个说法,下载了NHibernate 2.1.2。
SqlClientBatchingBatcher类中给command timeout赋值的代码如下:
可知取的是Cfg.Environment.CommandTimeout,而这个就对应的Global的配置。
而在DriveBase中对command timeout的赋值代码如下:
可知这里是将commandTimout直接赋值给了cmd.CommandTimeout,而commandTimeout是会读取hibernate.cfg.xml这个配置文件的。所以在hibernate.cfg.xml中配置的command timeout为300是会作用到这里。
解决办法
对于batch query timeout的异常,我们可以通过加长SqlClientBatchingBatcher的command timeout来解决。具体怎么加有两种方法:
在全局配置文件中配置NHibernate的command timeout,一般是App.config。
12345678910111213"1.0" encoding="utf-8" xml version=<configuration><configSections><section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"/></configSections><hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" ><session-factory><property name="adonet.batch_size">10</property><property name="command_timeout">300</property><property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property></session-factory></hibernate-configuration></configuration>在程序中,通过反射来设置全局的command timeout。
12345FieldInfo field = typeof(global::NHibernate.Cfg.Environment).GetField("GlobalProperties", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);Dictionary<string, string> gloablProperties = field.GetValue(null) as Dictionary<string, string>;gloablProperties.Add("command_timeout","300");
总结
在更改了全局配置之后,生产环境中的timeout得到了解决。所以有些时候还是要多从内部找问题的。